1 package org.apache.torque.betwixt;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.io.FileNotFoundException;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.net.URL;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.xml.sax.EntityResolver;
27 import org.xml.sax.InputSource;
28 import org.xml.sax.SAXException;
29
30 /***
31 * This class resolves the where to find the versions of the Betwixt Bean Map
32 * and DTD files to use in converting XML data to Torque OM objects. These
33 * files are found by using the methods described below.
34 * <p>
35 *
36 * Note: Both these files can be created from the Torque DB schema by using
37 * adding the 'Betwixt add-on' to Torque's Generator and using the datadtd task.
38 * <p>
39 *
40 * The DTD file is locate using the following method:
41 * <P>
42 *
43 * First, the URI on the XML Import file's DOCTYPE definition is parsed to get
44 * the specified file name (allows for DTD versioning). This name is combined
45 * with the value of the dtdPackage property and the classpath is searched. If
46 * this file is not found, the value specified by the dtdFileName property is
47 * used. If neither file is found, the normal SAX parser will attempt to load
48 * the DTD using the DOCTYPE URI
49 * <P>
50 *
51 * The Betwixt mapping file is found by searching the classpath for a file with
52 * the mapFileName located in the mapFilePackage.
53 * <p>
54 *
55 * @author <a href="mailto:greg.monroe@dukece.com">Greg Monroe</a>
56 */
57 public class Resolver implements EntityResolver
58 {
59 static Log logger = LogFactory.getLog(Resolver.class);
60
61 private String dtdPackage;
62 private String dtdFileName;
63 private String mapFilePackage;
64 private String mapFileName;
65 private String dtdUri;
66
67 /***
68 * A resolver to get the inputSource that a SAX parser will use for
69 * an import DTD definition.
70 */
71 public Resolver()
72 {
73 super();
74 }
75
76 public Resolver( String dtdPackage )
77 {
78 super();
79 setDtdPackage( dtdPackage);
80 }
81
82 /***
83 * Return an <code>InputSource</code> that provides access to the indicated
84 * entity. Assumes that systemId is in a URL format that ends in the dtd
85 * file name, e.g. "http://www.my.org/dtds/myDTD.dtd".<p>
86 *
87 * The file name and dtdPackage value are combine and the class path is
88 * searched for the DTD resource. If not found, a null is returned to
89 * indicate the parser should use it's normal resolving method.
90 *
91 * @param publicID a java.lang.String that contains the public identifier
92 * of the desired entity
93 * @param systemID a java.lang.String that contains the system identifier
94 * (i.e., the URL) of the desired entity
95 * @return org.xml.sax.InputSource
96 * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
97 * java.lang.String)
98 */
99 public InputSource resolveEntity( String publicID, String systemID )
100 throws SAXException, IOException
101 {
102 InputSource dtdSource = null;
103
104
105 String dtdName = systemID.substring(systemID.lastIndexOf('/')+1);
106 if ( dtdName != null && ! dtdName.equals(""))
107 {
108 if ( ! getDtdPackage().equals(""))
109 {
110 dtdName = getDtdPackage()+"/"+dtdName;
111 }
112
113 try
114 {
115 dtdSource = makeInputSource( dtdName );
116 }
117 catch ( Exception e )
118 {
119 String eMsg = "An unexpected error occured trying to " +
120 "locate DTD resource, '" + dtdName + "'";
121 logger.info(eMsg, e);
122 throw new SAXException( eMsg, e);
123 }
124 if ( dtdSource != null )
125 {
126 return dtdSource;
127 }
128 }
129
130
131 dtdName = getDtdFileName();
132 if ( dtdName != null && ! dtdName.equals(""))
133 {
134 if ( ! getDtdPackage().equals(""))
135 {
136 dtdName = getDtdPackage()+"/"+dtdName;
137 }
138
139 try
140 {
141 dtdSource = makeInputSource( dtdName );
142 }
143 catch ( Exception e )
144 {
145 String eMsg = "An unexpected error occured trying to " +
146 "locate DTD resource, '" + dtdName + "'";
147 logger.info(eMsg, e);
148 throw new SAXException( eMsg, e);
149 }
150 if ( dtdSource != null )
151 {
152 return dtdSource;
153 }
154 }
155 logger.warn("Could not resolve DTD file locally!");
156 return null;
157 }
158
159 /***
160 * Locate the required Betwixt map file on the classpath.
161 *
162 * @return The InputSource to read the mapping file from.
163 * @throws Exception
164 */
165 public InputSource resolveBetwixtFile( ) throws Exception
166 {
167 InputSource mapSource = null;
168 String fileName = getBewtixtFileName();
169 if ( fileName != null && ! fileName.equals(""))
170 {
171 try
172 {
173 mapSource = makeInputSource( fileName );
174 }
175 catch ( Exception e )
176 {
177 String eMsg = "An unexpected error occured trying to " +
178 "locate DTD resource, '" + fileName + "'";
179 logger.info(eMsg, e);
180 throw e;
181 }
182 if ( mapSource != null )
183 {
184 return mapSource;
185 }
186 }
187 String eMsg = "Could not resolve required Betwixt map file, '" +
188 fileName + "'.";
189 logger.error(eMsg);
190 throw new FileNotFoundException( eMsg );
191 }
192 /***
193 * Gets the correctly qualified Betwixt mapping file name to try to
194 * open via a resource lookup. E.g., packageDir/fileName or just
195 * fileName.
196 *
197 * @return The map file name to look for.
198 */
199 public String getBewtixtFileName() {
200 String fileName = getMapFileName();
201 if ( fileName != null && ! fileName.equals(""))
202 {
203 if ( ! getMapFilePackage().equals(""))
204 {
205 fileName = getMapFilePackage()+"/"+fileName;
206 }
207 }
208 return fileName;
209 }
210
211 /***
212 * Try to find the specified resource on the classpath.
213 *
214 * @param packageFileName
215 * @return The source or null if not found.
216 * @throws Exception
217 */
218 public InputSource makeInputSource( String packageFileName )/package-summary.html">ong> InputSource makeInputSource( String packageFileName )
219 throws Exception
220 {
221 InputSource source = null;
222
223 InputStream dtdStream = getClass().getClassLoader()
224 .getResourceAsStream(packageFileName);
225 if (dtdStream != null)
226 {
227 source = new InputSource(dtdStream);
228 return source;
229 }
230 else
231 {
232 logger.warn("Could not locate DTD file at " + packageFileName );
233 }
234 return source;
235 }
236
237 /***
238 * Resolve the DTD file's URI to use in DOCTYPE elements in the following
239 * manner:
240 * <p>
241 *
242 * If the dtdURI property is set, just use that. Otherwise, try to
243 * find the specified dtd file locally on the Classpath. If that
244 * fails, just the dtd file name.
245 * <p>
246 *
247 * @throws IllegalStateException if dtdFileName has not been set.
248 */
249 public String resolveDtdUri()
250 throws IllegalStateException
251 {
252
253 String uri = getDtdUri();
254 if ( uri != null )
255 {
256 return uri;
257 }
258
259 String fileName = getDtdFileName();
260 if ( fileName != null && ! fileName.equals(""))
261 {
262 if ( ! getDtdPackage().equals(""))
263 {
264 fileName = getDtdPackage()+"/"+fileName;
265 }
266 URL dtdURL = getClass().getClassLoader()
267 .getResource(fileName);
268 if (dtdURL != null)
269 {
270 return dtdURL.toString();
271 }
272
273 return getDtdFileName();
274 }
275 String eMsg = "getDtdURL() called before dtdFileName was set.";
276 logger.warn(eMsg);
277 throw new IllegalStateException(eMsg);
278 }
279
280 /***
281 * Get the file name of the DTD that that will be used to validate
282 * the XML import with.
283 *
284 * @return Returns the dtdFileName.
285 */
286 public String getDtdFileName()
287 {
288 return dtdFileName;
289 }
290
291 /***
292 * Set the file name (no path ) of the DTD file to validate
293 * the XML against.
294 *
295 * @param dtdFileName The dtdFileName to set.
296 */
297 public void setDtdFileName(String dtdFileName)
298 {
299 this.dtdFileName = dtdFileName;
300 }
301
302 /***
303 * Get the package directory the DTD will be located in. This
304 * will be a directory under the classpath or jar root,
305 * e.g. org/apache/torque/dtd
306 *
307 * @return Returns the dtdPackage or "" if not set.
308 */
309 public String getDtdPackage()
310 {
311 if ( this.dtdPackage == null )
312 {
313 this.dtdPackage = "";
314 }
315 return dtdPackage;
316 }
317
318 /***
319 * Set the package directory the DTD will be located in. This
320 * will be a directory under the classpath or jar root,
321 * e.g. org/apache/torque/dtd
322 *
323 * @param dtdPackage The dtdPackage to set
324 */
325 public void setDtdPackage(String dtdPackage)
326 {
327 if ( dtdPackage.startsWith("/"))
328 {
329 dtdPackage = dtdPackage.substring(1);
330 }
331 this.dtdPackage = dtdPackage;
332 }
333
334 /***
335 * Get the name of the Betwixt multi (bean) mapping file to be used. E.g.
336 * myDatabase.betwixt.
337 *
338 * @return Returns the mapFileName.
339 */
340 public String getMapFileName()
341 {
342 return mapFileName;
343 }
344
345 /***
346 * Set the name of the Betwixt multi (bean) mapping file to be used. E.g.
347 * myDatabase.betwixt.
348 *
349 * @param mapFileName The mapFileName to set.
350 */
351 public void setMapFileName(String mapFileName)
352 {
353 this.mapFileName = mapFileName;
354 }
355
356 /***
357 * Get the package directory the Betwixt mapping file will be located in.
358 * This will be a directory under the classpath or jar root,
359 * e.g. org/apache/torque/dtd
360 *
361 * @return Returns the mapFilePackage.
362 */
363 public String getMapFilePackage()
364 {
365 return mapFilePackage;
366 }
367
368 /***
369 * Set the package directory the Betwixt mapping file will be located in.
370 * This will be a directory under the classpath or jar root,
371 * e.g. org/apache/torque/dtd
372 *
373 * @param mapFilePackage The mapFilePackage to set.
374 */
375 public void setMapFilePackage(String mapFilePackage)
376 {
377 this.mapFilePackage = mapFilePackage;
378 }
379
380 /***
381 * Gets the URI to locate the DTD via the Internet (if any)
382 *
383 * @return Returns the dtdUri or null in not set.
384 */
385 public String getDtdUri()
386 {
387 return dtdUri;
388 }
389
390 /***
391 * Sets the URI to locate the DTD via the Internet (if any). This
392 * value will be used by resolveDtdUri() method if non-null.
393 *
394 * @param dtdURI The DTD URI to use.
395 */
396 public void setDtdUri(String dtdURI)
397 {
398 this.dtdUri = dtdURI;
399 }
400 }